package service

import (
	"cgs-server/helper"
	"cgs-server/server/base"
	"cgs-server/server/cgs/app/models"
	"cgs-server/server/cgs/app/models/dto"
	v1 "cgs-server/server/cgs/app/models/v1"
	"cgs-server/server/cgs/app/models/vo"
	"cgs-server/server/cgs/app/store"
	"cgs-server/server/model"
	"cgs-server/server/system/role"
	"errors"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"io/ioutil"
	"mime/multipart"
	"strconv"
	"strings"
)

type AppSrv interface {
	Get(appId string, getWorksheetInfo, simplify bool, user *model.User) (*vo.App, error)

	Update(app *dto.App, user *model.User) error

	Export(appId string, user *model.User) ([]*model.ZipFileData, error)

	Import(file *multipart.FileHeader, category, filePath string, user *model.User) error
}

type appSrv struct {
	store store.Factory
}

func (a *appSrv) Import(file *multipart.FileHeader, category, filePath string, user *model.User) error {
	var jsonFile []byte
	if file != nil {
		f, err := file.Open()
		if err != nil {
			return err
		}
		defer f.Close()
		jsonFile, err = ioutil.ReadAll(f)
		if err != nil {
			return err
		}
	} else if filePath == "" {
		err := errors.New("filePath or file is required")
		return err
	} else {
		body, err := helper.Get(filePath)
		if err != nil {
			return err
		}
		jsonFile = body
	}
	if len(jsonFile) == 0 {
		return errors.New("file data is empty")
	}
	zipData, err := base.ReadZipFile(jsonFile)
	if err != nil {
		return err
	}

	for _, data := range zipData {
		if data.Name != "app.json" {
			continue
		}
		var app vo.App
		if err := helper.FromJSON(data.Body, &app); err != nil {
			return err
		}

		// 1. 导入 app
		cgy, err := strconv.Atoi(category)
		if err != nil {
			cgy = -1
		}
		cgy2 := int32(cgy)

		dtoApp := dto.App{
			Id:                primitive.NewObjectID(),
			Title:             app.Title,
			BackgroundUrl:     app.BackgroundUrl,
			Category:          &cgy2,
			Password:          app.Password,
			Theme:             &app.Theme,
			Icon:              &app.Icon,
			Status:            app.Status,
			WorksheetGroupIds: app.WorksheetGroupIds,
		}
		dtoApp.TenantId = user.GetTenantId()
		dtoApp.Owner = user.GetId()

		err = a.store.App().Save(&dtoApp, user, v1.CreateOptions{})
		if err != nil {
			return err
		}

		// 添加该应用的管理员角色
		roleModel := &model.Role{
			RoleName:  "应用管理员",
			ParentId:  "0",
			Sort:      new(int),
			RoleAlias: "AppAdministrator",
			Desc:      "可以配置应用，管理应用下所有数据和人员",
			Type:      model.AppRole,
			VisualId:  dtoApp.GetId(),
		}
		roleService := role.NewRoleService()
		duModel := &model.DataUserModel{
			Data: roleModel,
			User: user,
		}
		err = roleService.Save(duModel)
		if err != nil {
			return err
		}

		// 更新用户角色关联表
		roleModel.UserIds = []string{user.GetId()}
		err = roleService.UpdateUser(duModel)
		if err != nil {
			return err
		}

		// 2. 导入分组
		groupIds := make([]string, 0, len(*app.WorksheetGroupList))
		formIdMap := make(map[string]string) // old, new
		groupFormIdsMap := make(map[string][]string, len(*app.WorksheetGroupList))
		for _, group := range *app.WorksheetGroupList {
			wg := models.WorksheetGroup{
				Id:    primitive.NewObjectID(),
				Name:  group.Name,
				AppId: dtoApp.GetId(),
			}
			err = a.store.WorksheetGroup().Save(&wg, user, v1.CreateOptions{})
			if err != nil {
				return err
			}
			// 记录 groupID 为后面自定义页面导入使用
			groupIds = append(groupIds, wg.GetId())

			// 3. 导入表单，长度是固定的
			formSlice := make([]string, len(*group.WorksheetInfoList))
			if len(*group.WorksheetInfoList) > 0 {
				for i, info := range *group.WorksheetInfoList {
					// 这里只导入表单
					if info.Type == nil || *info.Type == models.CustomPageType {
						continue
					}
					wsInfo := models.WorksheetInfo{
						Id:      primitive.ObjectID{},
						Name:    info.Name,
						Fields:  info.Fields,
						Config:  info.Config,
						AppId:   dtoApp.GetId(),
						GroupId: wg.GetId(),
						Type:    info.Type,
						Icon:    info.Icon,
					}
					err = a.store.WorksheetInfo().Save(&wsInfo, user, v1.CreateOptions{})
					if err != nil {
						return err
					}

					formIdMap[info.Id.Hex()] = wsInfo.GetId()
					formSlice[i] = wsInfo.GetId()

					// 4. 导入表单数据
					for _, datum := range zipData {
						if strings.Contains(datum.Name, info.Id.Hex()) {
							var rows bson.A
							err := helper.FromJSON(datum.Body, &rows)
							if err != nil {
								return err
							}

							var wsRows []*models.WorksheetRow
							for _, row := range rows {
								r := row.(map[string]interface{})
								var rowData = models.RowData{}
								for _, field := range *info.Fields {
									rowData[field.Name] = r[field.Name]
								}

								wsRow := models.WorksheetRow{
									RowId:       r["rowId"].(string),
									WorksheetId: wsInfo.GetId(),
									Data:        rowData,
								}

								err := wsRow.TransferData(info.Fields, user)
								if err != nil {
									return err
								}

								wsRows = append(wsRows, &wsRow)
							}

							err = a.store.WorksheetRows().BatchAddRow(wsRows, user, v1.CreateOptions{})
							if err != nil {
								return err
							}
						}
					}
				}
			}
			groupFormIdsMap[wg.GetId()] = formSlice

		}

		// 导入自定义页面，这里需要更新表单 NodeId
		for i, group := range *app.WorksheetGroupList {
			groupId := groupIds[i]
			// 3. 导入表单和自定义页面
			if len(*group.WorksheetInfoList) > 0 {
				for j, info := range *group.WorksheetInfoList {
					// 这里只导入自定义页面
					if info.Type == nil || *info.Type == models.WorksheetType {
						continue
					}
					// 替换表单 NodeId
					if info.Config != nil {
						if config, ok := info.Config.(string); ok {
							for oldId, newId := range formIdMap {
								config = strings.ReplaceAll(config, oldId, newId)
							}
							info.Config = config
						}
					}

					wsInfo := models.WorksheetInfo{
						Id:      primitive.ObjectID{},
						Name:    info.Name,
						Fields:  info.Fields,
						Config:  info.Config,
						AppId:   dtoApp.GetId(),
						GroupId: groupId,
						Type:    info.Type,
						Icon:    info.Icon,
					}
					err = a.store.WorksheetInfo().Save(&wsInfo, user, v1.CreateOptions{})
					if err != nil {
						return err
					}

					groupFormIdsMap[groupId][j] = wsInfo.GetId()
				}

				// 更新表单在分组中的顺序
				groupFormIds := groupFormIdsMap[groupId]
				hex, err := primitive.ObjectIDFromHex(groupId)
				if err != nil {
					return err
				}
				wg := &dto.WorksheetGroup{
					Id:               hex,
					WorksheetInfoIds: &groupFormIds,
				}
				err = a.store.WorksheetGroup().Update(wg, user, v1.UpdateOptions{})
				if err != nil {
					return err
				}
			}

		}
	}

	return nil
}

func (a *appSrv) Export(appId string, user *model.User) ([]*model.ZipFileData, error) {
	var zipData []*model.ZipFileData
	// 1. 获取app信息
	app, err := a.Get(appId, true, false, user)
	if err != nil {
		return nil, err
	}
	appBytes, err := helper.ToJSON(app)
	if err != nil {
		return nil, err
	}
	zipData = append(zipData, &model.ZipFileData{
		Name: "app.json",
		Body: appBytes,
	})

	// 2. 遍历分组信息
	if len(*app.WorksheetGroupList) == 0 {
		return zipData, nil
	}

	for _, group := range *app.WorksheetGroupList {
		if len(*group.WorksheetInfoList) == 0 {
			continue
		}
		for _, info := range *group.WorksheetInfoList {
			if *info.Type != 0 {
				continue
			}
			// 查询表单记录
			rowList, err := a.store.WorksheetRows().List(
				&models.WorksheetRow{WorksheetId: info.Id.Hex()},
				user,
				v1.ListOptions{},
			)

			if err != nil {
				return nil, err
			}

			rowBytes, err := helper.ToJSON(rowList)
			if err != nil {
				return nil, err
			}
			zipData = append(zipData, &model.ZipFileData{
				Name: fmt.Sprintf("forms/%s.json", info.Id.Hex()),
				Body: rowBytes,
			})
		}
	}

	return zipData, nil
}

func (a *appSrv) Update(app *dto.App, user *model.User) error {
	err := a.store.App().Update(app, user, v1.UpdateOptions{})
	return err
}

func (a *appSrv) Get(appId string, getWorksheetInfo, simplify bool, user *model.User) (*vo.App, error) {

	//if user == nil || !user.HasVisualAuth(appId) {
	//	return nil, errors.New("have no permission")
	//}

	appObjId, err := primitive.ObjectIDFromHex(appId)
	if err != nil {
		return nil, err
	}
	app, err := a.store.App().Get(&models.App{Id: appObjId}, user, v1.GetOptions{})
	if err != nil {
		return nil, err
	}

	// 查询所有分组
	opts := v1.ListOptions{}
	opts.AddFilter("appId", appId)
	groupList, err := a.store.WorksheetGroup().List(&models.WorksheetGroup{}, user, opts)
	if err != nil {
		return nil, err
	}

	// 如果没有分组，添加一个默认分组
	if len(*groupList) == 0 {
		wsGroup := &models.WorksheetGroup{
			Name:  "未命名分组",
			AppId: appId,
		}
		err = a.store.WorksheetGroup().Save(wsGroup, user, v1.CreateOptions{})
		if err != nil {
			return nil, err
		}
		*groupList = append(*groupList, &vo.WorksheetGroup{
			Id:    wsGroup.Id,
			Name:  wsGroup.Name,
			AppId: wsGroup.AppId,
		})
	}

	app.WorksheetGroupList = groupList

	// 排序
	if app.WorksheetGroupIds != nil && len(*app.WorksheetGroupIds) > 0 {
		appMap := make(map[string]*vo.WorksheetGroup, len(*groupList))
		for _, group := range *groupList {
			appMap[group.Id.Hex()] = group
		}

		newGroupList := vo.WorksheetGroupList{}
		for _, s := range *app.WorksheetGroupIds {
			if g, ok := appMap[s]; ok {
				newGroupList = append(newGroupList, g)
			}
		}
		app.WorksheetGroupList = &newGroupList
	}

	// 查询每个分组下的表单
	if getWorksheetInfo {
		for _, group := range *app.WorksheetGroupList {
			wsInfoOptions := v1.ListOptions{}
			wsInfoOptions.AddFilter("groupId", group.Id.Hex())
			infoList, err := a.store.WorksheetInfo().List(&models.WorksheetInfo{}, user, wsInfoOptions)
			if simplify {
				for _, info := range *infoList {
					info.Fields = nil
					info.Config = nil
				}
			}
			if err != nil {
				return nil, err
			}
			group.WorksheetInfoList = infoList
		}
	}

	return app, err
}

func newAppSrv(s *service) AppSrv {
	return &appSrv{
		store: s.store,
	}
}
